﻿using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace Gamemaster.Console
{
    public class RollCommand : Command
    {
        public const string FormulaRegexPattern = "^(\\d{0,3})?d(\\d{1,3})([+-]\\d{1,3})?$";

        public int NumberOfDice { get; set; }
        public int SidesOnDie { get; set; }
        public int Modifier { get; set; }
        public bool ShowDice { get; set; }
        public bool Explode { get; set; }
        public bool Wild { get; set; }
        public bool Botch { get; set; }
        public bool BotchWild { get; set; }

        public RollCommand(params string[] args) : base(args)
        {
        }

        public void ParseFormula(string formula)
        {
            NumberOfDice = 1;
            SidesOnDie = 6;
            Modifier = 0;

            if (TestFormula(formula))
            {
                Regex expression = new Regex(FormulaRegexPattern, RegexOptions.IgnoreCase);
                Match match = expression.Match(formula);

                Group numberGroup = match.Groups[1];        // Optional
                Group sidesGroup = match.Groups[2];         // Required
                Group modifierGroup = match.Groups[3];      // Optional

                SidesOnDie = int.Parse(sidesGroup.Value);

                if (!string.IsNullOrEmpty(numberGroup.Value))
                {
                    NumberOfDice = int.Parse(numberGroup.Value);
                }
                if (!string.IsNullOrEmpty(modifierGroup.Value))
                {
                    Modifier = int.Parse(modifierGroup.Value);
                }
            }
        }
        public override void PrintHelp()
        {
            base.PrintHelp();

            Out.WriteLine("Roll Command: Rolls dice");
            Out.WriteLine();
            Out.WriteLine("Syntax");
            Out.WriteLine("Roll <formula> [show] [wild | explode] [help]");
            Out.WriteLine();
            Out.WriteLine("formula:     a valid roll expression (3d6+2)");
            Out.WriteLine("show:        show all die roll results (optional)");
            Out.WriteLine("wild:        the last die in the set explodes (optional)");
            Out.WriteLine("explode:     any die that rolls max rolls again (optional)");
            Out.WriteLine("help:        print this text (optional)");
        }

        public static bool TestFormula(string parameters)
        {
            return Regex.IsMatch(parameters, FormulaRegexPattern);
        }

        protected override void Validate()
        {
            if (NumberOfDice == 0)
            {
                NumberOfDice = 1;
            }
            if (SidesOnDie == 0)
            {
                SidesOnDie = 6;
            }

            if (Wild && Explode)
            {
                throw new ArgumentException("Explode and Wild cannot both be applied to the same roll");
            }

            if (Botch && BotchWild)
            {
                throw new ArgumentException("Botch and BotchWild cannot both be applied to the same roll");
            }
        }
        protected override void RunCommand()
        {
            RollResults results = Dice.Roll(NumberOfDice).d(SidesOnDie).Plus(Modifier);

            if (Botch)
            {
                results.Botch();
            }
            if (Explode)
            {
                results.Explode();
            }
            if (Wild)
            {
                results.Wild();
            }
            if (BotchWild)
            {
                results.BotchWild();
            }

            if (ShowDice)
            {
                Out.WriteLine(results.ToString(ResultsFormat.ShowDice));
            }
            else
            {
                Out.WriteLine(results.ToString(ResultsFormat.SingleValue));
            }
        }
        protected override void HandleParameter(Parameter parameter)
        {
            parameter.Handled = true;

            switch (parameter.Value)
            {
                case "roll":
                    break;
                case "show":
                case "showdice":
                    ShowDice = true;
                    break;
                case "explode":
                    Explode = true;
                    break;
                case "wild":
                    Wild = true;
                    break;
                case "botch":
                    Botch = true;
                    break;
                case "botchwild":
                    BotchWild = true;
                    break;
                default:
                    if (TestFormula(parameter.Value))
                    {
                        ParseFormula(parameter.Value);
                    }
                    else
                    {
                        parameter.Handled = false;
                    }
                    break;
            }
        }
    }
}
